package avicit.bdp.oss.utils.upload.utils.redis;

import avicit.bdp.common.utils.SpringApplicationContext;
import avicit.bdp.oss.utils.upload.utils.common.CommUtil;
import avicit.bdp.oss.utils.upload.utils.common.Constant;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2020-12-09
 * @类说明：RedisLockUtils
 * @修改记录：
 * @注意事项：
 * @主要功能：Redis分布式锁
 */
public class RedisLock {
    private static RedisLock instance = new RedisLock();

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 私有构造函数
     */
    private RedisLock() {
        this.redisTemplate =  (RedisTemplate) SpringApplicationContext.getBean("redisTemplate");
    }

    /**
     * 单例模式
     * @return
     */
    public static RedisLock getInstance() {
        return instance;
    }

    /**
     * 获取锁
     * @param key
     * @return
     */
    public boolean lock(String key) {
        String lock = Constant.LOCK_PREFIX + key;

        return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
            long expireAt = System.currentTimeMillis() + Constant.LOCK_EXPIRE + 1;
            Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());

            if (acquire) {
                return true;
            }

            byte[] value = connection.get(lock.getBytes());
            if (Objects.nonNull(value) && value.length > 0) {

                long expireTime = Long.parseLong(new String(value));

                if (expireTime < System.currentTimeMillis()) {
                    // in case the lock is expired
                    byte[] oldValue = connection.getSet(lock.getBytes(),
                            String.valueOf(System.currentTimeMillis() + Constant.LOCK_EXPIRE + 1).getBytes());
                    // avoid dead lock
                    return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
                }
            }

            return false;
        });
    }

    /**
     * 释放锁
     * @param key
     */
    public void unlock(String key) {
//        redisTemplate.delete(key);
        redisTemplate.execute(new RedisCallback<Void>() {
            public Void doInRedis(RedisConnection jedis) throws DataAccessException {
                jedis.del(new byte[][]{CommUtil.encode(key)});
                return null;
            }
        });
    }
}
